/* Caprice32 - Amstrad CPC Emulator
   (c) Copyright 1997-2004 Ulrich Doewich

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/* Hitachi HD6845S CRT Controller (CRTC Type 0) emulation
   based on the CRTC emulation of WinAPE32 v2.0a5b by Richard Wilson

   Oct 16, 2000 - 23:12 started conversion from assembly to C
   Oct 17, 2000 - 19:25 finished converting main CRTC update loop
   Oct 17, 2000 - 22:04 added framework for mode draw handlers
   Oct 25, 2000 - 22:03 changed all CRTC counters to be only 8 bits wide; fixed address calculation
   Oct 30, 2000 - 19:03 fixed CPC screen address line advancement (test should have been for a _reset_ bit!)
   Mar 20, 2001 - 16:00 added draw_mode2
   Jun 20, 2001 - 23:24 added drawing routines for 32bpp video modes
   Jul 04, 2001 - 22:28 16bpp rendering; updated 8bpp code with VDU visible region limiting
   Sep 26, 2002 - 22:39 moved rendering code to separate files; added line doubling (in software) code
   Oct 07, 2002 - 21:58 removed the CPC.scr_max test in write_video_data; added support for variable line spacing

   May 23, 2004 - 17:38 added some preliminary VDU frame cropping to reduce the amount of data written to the video buffer
   May 24, 2004 - 00:44 moved the write_video_data code into the body of access_video_memory

   Jul 08, 2004 - 16:58 started implementing Richard's assembly CRTC emulation in C
*/

#include <math.h>

#include "cap32.h"
#include "crtc.h"
#include "z80.h"

extern t_CPC CPC;
extern t_CRTC CRTC;
extern t_GateArray GateArray;
extern t_VDU VDU;
extern t_z80regs z80;

extern dword dwXScale;
extern byte *pbRAM;

#ifdef DEBUG_CRTC
extern dword dwDebugFlag;
extern FILE *pfoDebug;
#endif

#define MAX_SYNC_DEC 80
#define MAX_SYNC_INC 80

#define MAX_DRAWN 270 // Max displayed scan line (+1)

#define MIN_VHOLD 250
#define MAX_VHOLD 380
#define MID_VHOLD 295
#define MIN_VHOLD_RANGE 46
#define MAX_VHOLD_RANGE 74

t_flags1 flags1;
t_new_dt new_dt;

dword LastPreRend;
word MinVSync, MaxVSync;
int iMonHSPeakPos, iMonHSStartPos, iMonHSEndPos, iMonHSPeakToStart, iMonHSStartToPeak, iMonHSEndToPeak, iMonHSPeakToEnd;
int HorzPos, MonHSYNC, MonFreeSync;
int HSyncDuration, MinHSync, MaxHSync;
int HadP;
byte PosShift, HorzChar, HorzMax;

dword *ModeMaps[4];
dword *ModeMap;
byte HorzPix[49];
byte RendBuff[800];
byte *RendWid, *RendOut;
dword *RendStart, *RendPos;

word MAXlate[0x7400];

void (*PreRender)(void);

// Version 2 translation tables - static
dword M0Map[0x200] = {
   0x00000000,0x00000000,0x00000000,0x08080808,0x08080808,0x00000000,0x08080808,0x08080808,
   0x00000000,0x02020202,0x00000000,0x0A0A0A0A,0x08080808,0x02020202,0x08080808,0x0A0A0A0A,
   0x02020202,0x00000000,0x02020202,0x08080808,0x0A0A0A0A,0x00000000,0x0A0A0A0A,0x08080808,
   0x02020202,0x02020202,0x02020202,0x0A0A0A0A,0x0A0A0A0A,0x02020202,0x0A0A0A0A,0x0A0A0A0A,
   0x00000000,0x04040404,0x00000000,0x0C0C0C0C,0x08080808,0x04040404,0x08080808,0x0C0C0C0C,
   0x00000000,0x06060606,0x00000000,0x0E0E0E0E,0x08080808,0x06060606,0x08080808,0x0E0E0E0E,
   0x02020202,0x04040404,0x02020202,0x0C0C0C0C,0x0A0A0A0A,0x04040404,0x0A0A0A0A,0x0C0C0C0C,
   0x02020202,0x06060606,0x02020202,0x0E0E0E0E,0x0A0A0A0A,0x06060606,0x0A0A0A0A,0x0E0E0E0E,
   0x04040404,0x00000000,0x04040404,0x08080808,0x0C0C0C0C,0x00000000,0x0C0C0C0C,0x08080808,
   0x04040404,0x02020202,0x04040404,0x0A0A0A0A,0x0C0C0C0C,0x02020202,0x0C0C0C0C,0x0A0A0A0A,
   0x06060606,0x00000000,0x06060606,0x08080808,0x0E0E0E0E,0x00000000,0x0E0E0E0E,0x08080808,
   0x06060606,0x02020202,0x06060606,0x0A0A0A0A,0x0E0E0E0E,0x02020202,0x0E0E0E0E,0x0A0A0A0A,
   0x04040404,0x04040404,0x04040404,0x0C0C0C0C,0x0C0C0C0C,0x04040404,0x0C0C0C0C,0x0C0C0C0C,
   0x04040404,0x06060606,0x04040404,0x0E0E0E0E,0x0C0C0C0C,0x06060606,0x0C0C0C0C,0x0E0E0E0E,
   0x06060606,0x04040404,0x06060606,0x0C0C0C0C,0x0E0E0E0E,0x04040404,0x0E0E0E0E,0x0C0C0C0C,
   0x06060606,0x06060606,0x06060606,0x0E0E0E0E,0x0E0E0E0E,0x06060606,0x0E0E0E0E,0x0E0E0E0E,
   0x00000000,0x01010101,0x00000000,0x09090909,0x08080808,0x01010101,0x08080808,0x09090909,
   0x00000000,0x03030303,0x00000000,0x0B0B0B0B,0x08080808,0x03030303,0x08080808,0x0B0B0B0B,
   0x02020202,0x01010101,0x02020202,0x09090909,0x0A0A0A0A,0x01010101,0x0A0A0A0A,0x09090909,
   0x02020202,0x03030303,0x02020202,0x0B0B0B0B,0x0A0A0A0A,0x03030303,0x0A0A0A0A,0x0B0B0B0B,
   0x00000000,0x05050505,0x00000000,0x0D0D0D0D,0x08080808,0x05050505,0x08080808,0x0D0D0D0D,
   0x00000000,0x07070707,0x00000000,0x0F0F0F0F,0x08080808,0x07070707,0x08080808,0x0F0F0F0F,
   0x02020202,0x05050505,0x02020202,0x0D0D0D0D,0x0A0A0A0A,0x05050505,0x0A0A0A0A,0x0D0D0D0D,
   0x02020202,0x07070707,0x02020202,0x0F0F0F0F,0x0A0A0A0A,0x07070707,0x0A0A0A0A,0x0F0F0F0F,
   0x04040404,0x01010101,0x04040404,0x09090909,0x0C0C0C0C,0x01010101,0x0C0C0C0C,0x09090909,
   0x04040404,0x03030303,0x04040404,0x0B0B0B0B,0x0C0C0C0C,0x03030303,0x0C0C0C0C,0x0B0B0B0B,
   0x06060606,0x01010101,0x06060606,0x09090909,0x0E0E0E0E,0x01010101,0x0E0E0E0E,0x09090909,
   0x06060606,0x03030303,0x06060606,0x0B0B0B0B,0x0E0E0E0E,0x03030303,0x0E0E0E0E,0x0B0B0B0B,
   0x04040404,0x05050505,0x04040404,0x0D0D0D0D,0x0C0C0C0C,0x05050505,0x0C0C0C0C,0x0D0D0D0D,
   0x04040404,0x07070707,0x04040404,0x0F0F0F0F,0x0C0C0C0C,0x07070707,0x0C0C0C0C,0x0F0F0F0F,
   0x06060606,0x05050505,0x06060606,0x0D0D0D0D,0x0E0E0E0E,0x05050505,0x0E0E0E0E,0x0D0D0D0D,
   0x06060606,0x07070707,0x06060606,0x0F0F0F0F,0x0E0E0E0E,0x07070707,0x0E0E0E0E,0x0F0F0F0F,
   0x01010101,0x00000000,0x01010101,0x08080808,0x09090909,0x00000000,0x09090909,0x08080808,
   0x01010101,0x02020202,0x01010101,0x0A0A0A0A,0x09090909,0x02020202,0x09090909,0x0A0A0A0A,
   0x03030303,0x00000000,0x03030303,0x08080808,0x0B0B0B0B,0x00000000,0x0B0B0B0B,0x08080808,
   0x03030303,0x02020202,0x03030303,0x0A0A0A0A,0x0B0B0B0B,0x02020202,0x0B0B0B0B,0x0A0A0A0A,
   0x01010101,0x04040404,0x01010101,0x0C0C0C0C,0x09090909,0x04040404,0x09090909,0x0C0C0C0C,
   0x01010101,0x06060606,0x01010101,0x0E0E0E0E,0x09090909,0x06060606,0x09090909,0x0E0E0E0E,
   0x03030303,0x04040404,0x03030303,0x0C0C0C0C,0x0B0B0B0B,0x04040404,0x0B0B0B0B,0x0C0C0C0C,
   0x03030303,0x06060606,0x03030303,0x0E0E0E0E,0x0B0B0B0B,0x06060606,0x0B0B0B0B,0x0E0E0E0E,
   0x05050505,0x00000000,0x05050505,0x08080808,0x0D0D0D0D,0x00000000,0x0D0D0D0D,0x08080808,
   0x05050505,0x02020202,0x05050505,0x0A0A0A0A,0x0D0D0D0D,0x02020202,0x0D0D0D0D,0x0A0A0A0A,
   0x07070707,0x00000000,0x07070707,0x08080808,0x0F0F0F0F,0x00000000,0x0F0F0F0F,0x08080808,
   0x07070707,0x02020202,0x07070707,0x0A0A0A0A,0x0F0F0F0F,0x02020202,0x0F0F0F0F,0x0A0A0A0A,
   0x05050505,0x04040404,0x05050505,0x0C0C0C0C,0x0D0D0D0D,0x04040404,0x0D0D0D0D,0x0C0C0C0C,
   0x05050505,0x06060606,0x05050505,0x0E0E0E0E,0x0D0D0D0D,0x06060606,0x0D0D0D0D,0x0E0E0E0E,
   0x07070707,0x04040404,0x07070707,0x0C0C0C0C,0x0F0F0F0F,0x04040404,0x0F0F0F0F,0x0C0C0C0C,
   0x07070707,0x06060606,0x07070707,0x0E0E0E0E,0x0F0F0F0F,0x06060606,0x0F0F0F0F,0x0E0E0E0E,
   0x01010101,0x01010101,0x01010101,0x09090909,0x09090909,0x01010101,0x09090909,0x09090909,
   0x01010101,0x03030303,0x01010101,0x0B0B0B0B,0x09090909,0x03030303,0x09090909,0x0B0B0B0B,
   0x03030303,0x01010101,0x03030303,0x09090909,0x0B0B0B0B,0x01010101,0x0B0B0B0B,0x09090909,
   0x03030303,0x03030303,0x03030303,0x0B0B0B0B,0x0B0B0B0B,0x03030303,0x0B0B0B0B,0x0B0B0B0B,
   0x01010101,0x05050505,0x01010101,0x0D0D0D0D,0x09090909,0x05050505,0x09090909,0x0D0D0D0D,
   0x01010101,0x07070707,0x01010101,0x0F0F0F0F,0x09090909,0x07070707,0x09090909,0x0F0F0F0F,
   0x03030303,0x05050505,0x03030303,0x0D0D0D0D,0x0B0B0B0B,0x05050505,0x0B0B0B0B,0x0D0D0D0D,
   0x03030303,0x07070707,0x03030303,0x0F0F0F0F,0x0B0B0B0B,0x07070707,0x0B0B0B0B,0x0F0F0F0F,
   0x05050505,0x01010101,0x05050505,0x09090909,0x0D0D0D0D,0x01010101,0x0D0D0D0D,0x09090909,
   0x05050505,0x03030303,0x05050505,0x0B0B0B0B,0x0D0D0D0D,0x03030303,0x0D0D0D0D,0x0B0B0B0B,
   0x07070707,0x01010101,0x07070707,0x09090909,0x0F0F0F0F,0x01010101,0x0F0F0F0F,0x09090909,
   0x07070707,0x03030303,0x07070707,0x0B0B0B0B,0x0F0F0F0F,0x03030303,0x0F0F0F0F,0x0B0B0B0B,
   0x05050505,0x05050505,0x05050505,0x0D0D0D0D,0x0D0D0D0D,0x05050505,0x0D0D0D0D,0x0D0D0D0D,
   0x05050505,0x07070707,0x05050505,0x0F0F0F0F,0x0D0D0D0D,0x07070707,0x0D0D0D0D,0x0F0F0F0F,
   0x07070707,0x05050505,0x07070707,0x0D0D0D0D,0x0F0F0F0F,0x05050505,0x0F0F0F0F,0x0D0D0D0D,
   0x07070707,0x07070707,0x07070707,0x0F0F0F0F,0x0F0F0F0F,0x07070707,0x0F0F0F0F,0x0F0F0F0F
};

dword M1Map[0x200] = {
   0x00000000,0x00000000,0x00000000,0x02020000,0x00000000,0x00000202,0x00000000,0x02020202,
   0x02020000,0x00000000,0x02020000,0x02020000,0x02020000,0x00000202,0x02020000,0x02020202,
   0x00000202,0x00000000,0x00000202,0x02020000,0x00000202,0x00000202,0x00000202,0x02020202,
   0x02020202,0x00000000,0x02020202,0x02020000,0x02020202,0x00000202,0x02020202,0x02020202,
   0x00000000,0x01010000,0x00000000,0x03030000,0x00000000,0x01010202,0x00000000,0x03030202,
   0x02020000,0x01010000,0x02020000,0x03030000,0x02020000,0x01010202,0x02020000,0x03030202,
   0x00000202,0x01010000,0x00000202,0x03030000,0x00000202,0x01010202,0x00000202,0x03030202,
   0x02020202,0x01010000,0x02020202,0x03030000,0x02020202,0x01010202,0x02020202,0x03030202,
   0x00000000,0x00000101,0x00000000,0x02020101,0x00000000,0x00000303,0x00000000,0x02020303,
   0x02020000,0x00000101,0x02020000,0x02020101,0x02020000,0x00000303,0x02020000,0x02020303,
   0x00000202,0x00000101,0x00000202,0x02020101,0x00000202,0x00000303,0x00000202,0x02020303,
   0x02020202,0x00000101,0x02020202,0x02020101,0x02020202,0x00000303,0x02020202,0x02020303,
   0x00000000,0x01010101,0x00000000,0x03030101,0x00000000,0x01010303,0x00000000,0x03030303,
   0x02020000,0x01010101,0x02020000,0x03030101,0x02020000,0x01010303,0x02020000,0x03030303,
   0x00000202,0x01010101,0x00000202,0x03030101,0x00000202,0x01010303,0x00000202,0x03030303,
   0x02020202,0x01010101,0x02020202,0x03030101,0x02020202,0x01010303,0x02020202,0x03030303,
   0x01010000,0x00000000,0x01010000,0x02020000,0x01010000,0x00000202,0x01010000,0x02020202,
   0x03030000,0x00000000,0x03030000,0x02020000,0x03030000,0x00000202,0x03030000,0x02020202,
   0x01010202,0x00000000,0x01010202,0x02020000,0x01010202,0x00000202,0x01010202,0x02020202,
   0x03030202,0x00000000,0x03030202,0x02020000,0x03030202,0x00000202,0x03030202,0x02020202,
   0x01010000,0x01010000,0x01010000,0x03030000,0x01010000,0x01010202,0x01010000,0x03030202,
   0x03030000,0x01010000,0x03030000,0x03030000,0x03030000,0x01010202,0x03030000,0x03030202,
   0x01010202,0x01010000,0x01010202,0x03030000,0x01010202,0x01010202,0x01010202,0x03030202,
   0x03030202,0x01010000,0x03030202,0x03030000,0x03030202,0x01010202,0x03030202,0x03030202,
   0x01010000,0x00000101,0x01010000,0x02020101,0x01010000,0x00000303,0x01010000,0x02020303,
   0x03030000,0x00000101,0x03030000,0x02020101,0x03030000,0x00000303,0x03030000,0x02020303,
   0x01010202,0x00000101,0x01010202,0x02020101,0x01010202,0x00000303,0x01010202,0x02020303,
   0x03030202,0x00000101,0x03030202,0x02020101,0x03030202,0x00000303,0x03030202,0x02020303,
   0x01010000,0x01010101,0x01010000,0x03030101,0x01010000,0x01010303,0x01010000,0x03030303,
   0x03030000,0x01010101,0x03030000,0x03030101,0x03030000,0x01010303,0x03030000,0x03030303,
   0x01010202,0x01010101,0x01010202,0x03030101,0x01010202,0x01010303,0x01010202,0x03030303,
   0x03030202,0x01010101,0x03030202,0x03030101,0x03030202,0x01010303,0x03030202,0x03030303,
   0x00000101,0x00000000,0x00000101,0x02020000,0x00000101,0x00000202,0x00000101,0x02020202,
   0x02020101,0x00000000,0x02020101,0x02020000,0x02020101,0x00000202,0x02020101,0x02020202,
   0x00000303,0x00000000,0x00000303,0x02020000,0x00000303,0x00000202,0x00000303,0x02020202,
   0x02020303,0x00000000,0x02020303,0x02020000,0x02020303,0x00000202,0x02020303,0x02020202,
   0x00000101,0x01010000,0x00000101,0x03030000,0x00000101,0x01010202,0x00000101,0x03030202,
   0x02020101,0x01010000,0x02020101,0x03030000,0x02020101,0x01010202,0x02020101,0x03030202,
   0x00000303,0x01010000,0x00000303,0x03030000,0x00000303,0x01010202,0x00000303,0x03030202,
   0x02020303,0x01010000,0x02020303,0x03030000,0x02020303,0x01010202,0x02020303,0x03030202,
   0x00000101,0x00000101,0x00000101,0x02020101,0x00000101,0x00000303,0x00000101,0x02020303,
   0x02020101,0x00000101,0x02020101,0x02020101,0x02020101,0x00000303,0x02020101,0x02020303,
   0x00000303,0x00000101,0x00000303,0x02020101,0x00000303,0x00000303,0x00000303,0x02020303,
   0x02020303,0x00000101,0x02020303,0x02020101,0x02020303,0x00000303,0x02020303,0x02020303,
   0x00000101,0x01010101,0x00000101,0x03030101,0x00000101,0x01010303,0x00000101,0x03030303,
   0x02020101,0x01010101,0x02020101,0x03030101,0x02020101,0x01010303,0x02020101,0x03030303,
   0x00000303,0x01010101,0x00000303,0x03030101,0x00000303,0x01010303,0x00000303,0x03030303,
   0x02020303,0x01010101,0x02020303,0x03030101,0x02020303,0x01010303,0x02020303,0x03030303,
   0x01010101,0x00000000,0x01010101,0x02020000,0x01010101,0x00000202,0x01010101,0x02020202,
   0x03030101,0x00000000,0x03030101,0x02020000,0x03030101,0x00000202,0x03030101,0x02020202,
   0x01010303,0x00000000,0x01010303,0x02020000,0x01010303,0x00000202,0x01010303,0x02020202,
   0x03030303,0x00000000,0x03030303,0x02020000,0x03030303,0x00000202,0x03030303,0x02020202,
   0x01010101,0x01010000,0x01010101,0x03030000,0x01010101,0x01010202,0x01010101,0x03030202,
   0x03030101,0x01010000,0x03030101,0x03030000,0x03030101,0x01010202,0x03030101,0x03030202,
   0x01010303,0x01010000,0x01010303,0x03030000,0x01010303,0x01010202,0x01010303,0x03030202,
   0x03030303,0x01010000,0x03030303,0x03030000,0x03030303,0x01010202,0x03030303,0x03030202,
   0x01010101,0x00000101,0x01010101,0x02020101,0x01010101,0x00000303,0x01010101,0x02020303,
   0x03030101,0x00000101,0x03030101,0x02020101,0x03030101,0x00000303,0x03030101,0x02020303,
   0x01010303,0x00000101,0x01010303,0x02020101,0x01010303,0x00000303,0x01010303,0x02020303,
   0x03030303,0x00000101,0x03030303,0x02020101,0x03030303,0x00000303,0x03030303,0x02020303,
   0x01010101,0x01010101,0x01010101,0x03030101,0x01010101,0x01010303,0x01010101,0x03030303,
   0x03030101,0x01010101,0x03030101,0x03030101,0x03030101,0x01010303,0x03030101,0x03030303,
   0x01010303,0x01010101,0x01010303,0x03030101,0x01010303,0x01010303,0x01010303,0x03030303,
   0x03030303,0x01010101,0x03030303,0x03030101,0x03030303,0x01010303,0x03030303,0x03030303
};

dword M2Map[0x200] = {
   0x00000000,0x00000000,0x00000000,0x01000000,0x00000000,0x00010000,0x00000000,0x01010000,
   0x00000000,0x00000100,0x00000000,0x01000100,0x00000000,0x00010100,0x00000000,0x01010100,
   0x00000000,0x00000001,0x00000000,0x01000001,0x00000000,0x00010001,0x00000000,0x01010001,
   0x00000000,0x00000101,0x00000000,0x01000101,0x00000000,0x00010101,0x00000000,0x01010101,
   0x01000000,0x00000000,0x01000000,0x01000000,0x01000000,0x00010000,0x01000000,0x01010000,
   0x01000000,0x00000100,0x01000000,0x01000100,0x01000000,0x00010100,0x01000000,0x01010100,
   0x01000000,0x00000001,0x01000000,0x01000001,0x01000000,0x00010001,0x01000000,0x01010001,
   0x01000000,0x00000101,0x01000000,0x01000101,0x01000000,0x00010101,0x01000000,0x01010101,
   0x00010000,0x00000000,0x00010000,0x01000000,0x00010000,0x00010000,0x00010000,0x01010000,
   0x00010000,0x00000100,0x00010000,0x01000100,0x00010000,0x00010100,0x00010000,0x01010100,
   0x00010000,0x00000001,0x00010000,0x01000001,0x00010000,0x00010001,0x00010000,0x01010001,
   0x00010000,0x00000101,0x00010000,0x01000101,0x00010000,0x00010101,0x00010000,0x01010101,
   0x01010000,0x00000000,0x01010000,0x01000000,0x01010000,0x00010000,0x01010000,0x01010000,
   0x01010000,0x00000100,0x01010000,0x01000100,0x01010000,0x00010100,0x01010000,0x01010100,
   0x01010000,0x00000001,0x01010000,0x01000001,0x01010000,0x00010001,0x01010000,0x01010001,
   0x01010000,0x00000101,0x01010000,0x01000101,0x01010000,0x00010101,0x01010000,0x01010101,
   0x00000100,0x00000000,0x00000100,0x01000000,0x00000100,0x00010000,0x00000100,0x01010000,
   0x00000100,0x00000100,0x00000100,0x01000100,0x00000100,0x00010100,0x00000100,0x01010100,
   0x00000100,0x00000001,0x00000100,0x01000001,0x00000100,0x00010001,0x00000100,0x01010001,
   0x00000100,0x00000101,0x00000100,0x01000101,0x00000100,0x00010101,0x00000100,0x01010101,
   0x01000100,0x00000000,0x01000100,0x01000000,0x01000100,0x00010000,0x01000100,0x01010000,
   0x01000100,0x00000100,0x01000100,0x01000100,0x01000100,0x00010100,0x01000100,0x01010100,
   0x01000100,0x00000001,0x01000100,0x01000001,0x01000100,0x00010001,0x01000100,0x01010001,
   0x01000100,0x00000101,0x01000100,0x01000101,0x01000100,0x00010101,0x01000100,0x01010101,
   0x00010100,0x00000000,0x00010100,0x01000000,0x00010100,0x00010000,0x00010100,0x01010000,
   0x00010100,0x00000100,0x00010100,0x01000100,0x00010100,0x00010100,0x00010100,0x01010100,
   0x00010100,0x00000001,0x00010100,0x01000001,0x00010100,0x00010001,0x00010100,0x01010001,
   0x00010100,0x00000101,0x00010100,0x01000101,0x00010100,0x00010101,0x00010100,0x01010101,
   0x01010100,0x00000000,0x01010100,0x01000000,0x01010100,0x00010000,0x01010100,0x01010000,
   0x01010100,0x00000100,0x01010100,0x01000100,0x01010100,0x00010100,0x01010100,0x01010100,
   0x01010100,0x00000001,0x01010100,0x01000001,0x01010100,0x00010001,0x01010100,0x01010001,
   0x01010100,0x00000101,0x01010100,0x01000101,0x01010100,0x00010101,0x01010100,0x01010101,
   0x00000001,0x00000000,0x00000001,0x01000000,0x00000001,0x00010000,0x00000001,0x01010000,
   0x00000001,0x00000100,0x00000001,0x01000100,0x00000001,0x00010100,0x00000001,0x01010100,
   0x00000001,0x00000001,0x00000001,0x01000001,0x00000001,0x00010001,0x00000001,0x01010001,
   0x00000001,0x00000101,0x00000001,0x01000101,0x00000001,0x00010101,0x00000001,0x01010101,
   0x01000001,0x00000000,0x01000001,0x01000000,0x01000001,0x00010000,0x01000001,0x01010000,
   0x01000001,0x00000100,0x01000001,0x01000100,0x01000001,0x00010100,0x01000001,0x01010100,
   0x01000001,0x00000001,0x01000001,0x01000001,0x01000001,0x00010001,0x01000001,0x01010001,
   0x01000001,0x00000101,0x01000001,0x01000101,0x01000001,0x00010101,0x01000001,0x01010101,
   0x00010001,0x00000000,0x00010001,0x01000000,0x00010001,0x00010000,0x00010001,0x01010000,
   0x00010001,0x00000100,0x00010001,0x01000100,0x00010001,0x00010100,0x00010001,0x01010100,
   0x00010001,0x00000001,0x00010001,0x01000001,0x00010001,0x00010001,0x00010001,0x01010001,
   0x00010001,0x00000101,0x00010001,0x01000101,0x00010001,0x00010101,0x00010001,0x01010101,
   0x01010001,0x00000000,0x01010001,0x01000000,0x01010001,0x00010000,0x01010001,0x01010000,
   0x01010001,0x00000100,0x01010001,0x01000100,0x01010001,0x00010100,0x01010001,0x01010100,
   0x01010001,0x00000001,0x01010001,0x01000001,0x01010001,0x00010001,0x01010001,0x01010001,
   0x01010001,0x00000101,0x01010001,0x01000101,0x01010001,0x00010101,0x01010001,0x01010101,
   0x00000101,0x00000000,0x00000101,0x01000000,0x00000101,0x00010000,0x00000101,0x01010000,
   0x00000101,0x00000100,0x00000101,0x01000100,0x00000101,0x00010100,0x00000101,0x01010100,
   0x00000101,0x00000001,0x00000101,0x01000001,0x00000101,0x00010001,0x00000101,0x01010001,
   0x00000101,0x00000101,0x00000101,0x01000101,0x00000101,0x00010101,0x00000101,0x01010101,
   0x01000101,0x00000000,0x01000101,0x01000000,0x01000101,0x00010000,0x01000101,0x01010000,
   0x01000101,0x00000100,0x01000101,0x01000100,0x01000101,0x00010100,0x01000101,0x01010100,
   0x01000101,0x00000001,0x01000101,0x01000001,0x01000101,0x00010001,0x01000101,0x01010001,
   0x01000101,0x00000101,0x01000101,0x01000101,0x01000101,0x00010101,0x01000101,0x01010101,
   0x00010101,0x00000000,0x00010101,0x01000000,0x00010101,0x00010000,0x00010101,0x01010000,
   0x00010101,0x00000100,0x00010101,0x01000100,0x00010101,0x00010100,0x00010101,0x01010100,
   0x00010101,0x00000001,0x00010101,0x01000001,0x00010101,0x00010001,0x00010101,0x01010001,
   0x00010101,0x00000101,0x00010101,0x01000101,0x00010101,0x00010101,0x00010101,0x01010101,
   0x01010101,0x00000000,0x01010101,0x01000000,0x01010101,0x00010000,0x01010101,0x01010000,
   0x01010101,0x00000100,0x01010101,0x01000100,0x01010101,0x00010100,0x01010101,0x01010100,
   0x01010101,0x00000001,0x01010101,0x01000001,0x01010101,0x00010001,0x01010101,0x01010001,
   0x01010101,0x00000101,0x01010101,0x01000101,0x01010101,0x00010101,0x01010101,0x01010101
};

dword M3Map[0x200] = {
   0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
   0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,
   0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,
   0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,
   0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
   0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,
   0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,
   0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,
   0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
   0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,
   0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,
   0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,
   0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
   0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,
   0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,
   0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,
   0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,
   0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,
   0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,
   0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,
   0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,
   0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,
   0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,
   0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,
   0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,
   0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,
   0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,
   0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,
   0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,
   0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,
   0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,
   0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,
   0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,
   0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,
   0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,
   0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,
   0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,
   0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,
   0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,
   0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,
   0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,
   0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,
   0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,
   0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,
   0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,
   0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,
   0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,
   0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,
   0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,
   0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,
   0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,
   0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,
   0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,
   0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,
   0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,
   0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,
   0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,
   0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,
   0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,
   0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,
   0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,
   0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,
   0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,
   0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303
};

dword M0hMap[0x100] = {
   0x00000000,0x08080000,0x00000808,0x08080808,0x02020000,0x0A0A0000,0x02020808,0x0A0A0808,
   0x00000202,0x08080202,0x00000A0A,0x08080A0A,0x02020202,0x0A0A0202,0x02020A0A,0x0A0A0A0A,
   0x04040000,0x0C0C0000,0x04040808,0x0C0C0808,0x06060000,0x0E0E0000,0x06060808,0x0E0E0808,
   0x04040202,0x0C0C0202,0x04040A0A,0x0C0C0A0A,0x06060202,0x0E0E0202,0x06060A0A,0x0E0E0A0A,
   0x00000404,0x08080404,0x00000C0C,0x08080C0C,0x02020404,0x0A0A0404,0x02020C0C,0x0A0A0C0C,
   0x00000606,0x08080606,0x00000E0E,0x08080E0E,0x02020606,0x0A0A0606,0x02020E0E,0x0A0A0E0E,
   0x04040404,0x0C0C0404,0x04040C0C,0x0C0C0C0C,0x06060404,0x0E0E0404,0x06060C0C,0x0E0E0C0C,
   0x04040606,0x0C0C0606,0x04040E0E,0x0C0C0E0E,0x06060606,0x0E0E0606,0x06060E0E,0x0E0E0E0E,
   0x01010000,0x09090000,0x01010808,0x09090808,0x03030000,0x0B0B0000,0x03030808,0x0B0B0808,
   0x01010202,0x09090202,0x01010A0A,0x09090A0A,0x03030202,0x0B0B0202,0x03030A0A,0x0B0B0A0A,
   0x05050000,0x0D0D0000,0x05050808,0x0D0D0808,0x07070000,0x0F0F0000,0x07070808,0x0F0F0808,
   0x05050202,0x0D0D0202,0x05050A0A,0x0D0D0A0A,0x07070202,0x0F0F0202,0x07070A0A,0x0F0F0A0A,
   0x01010404,0x09090404,0x01010C0C,0x09090C0C,0x03030404,0x0B0B0404,0x03030C0C,0x0B0B0C0C,
   0x01010606,0x09090606,0x01010E0E,0x09090E0E,0x03030606,0x0B0B0606,0x03030E0E,0x0B0B0E0E,
   0x05050404,0x0D0D0404,0x05050C0C,0x0D0D0C0C,0x07070404,0x0F0F0404,0x07070C0C,0x0F0F0C0C,
   0x05050606,0x0D0D0606,0x05050E0E,0x0D0D0E0E,0x07070606,0x0F0F0606,0x07070E0E,0x0F0F0E0E,
   0x00000101,0x08080101,0x00000909,0x08080909,0x02020101,0x0A0A0101,0x02020909,0x0A0A0909,
   0x00000303,0x08080303,0x00000B0B,0x08080B0B,0x02020303,0x0A0A0303,0x02020B0B,0x0A0A0B0B,
   0x04040101,0x0C0C0101,0x04040909,0x0C0C0909,0x06060101,0x0E0E0101,0x06060909,0x0E0E0909,
   0x04040303,0x0C0C0303,0x04040B0B,0x0C0C0B0B,0x06060303,0x0E0E0303,0x06060B0B,0x0E0E0B0B,
   0x00000505,0x08080505,0x00000D0D,0x08080D0D,0x02020505,0x0A0A0505,0x02020D0D,0x0A0A0D0D,
   0x00000707,0x08080707,0x00000F0F,0x08080F0F,0x02020707,0x0A0A0707,0x02020F0F,0x0A0A0F0F,
   0x04040505,0x0C0C0505,0x04040D0D,0x0C0C0D0D,0x06060505,0x0E0E0505,0x06060D0D,0x0E0E0D0D,
   0x04040707,0x0C0C0707,0x04040F0F,0x0C0C0F0F,0x06060707,0x0E0E0707,0x06060F0F,0x0E0E0F0F,
   0x01010101,0x09090101,0x01010909,0x09090909,0x03030101,0x0B0B0101,0x03030909,0x0B0B0909,
   0x01010303,0x09090303,0x01010B0B,0x09090B0B,0x03030303,0x0B0B0303,0x03030B0B,0x0B0B0B0B,
   0x05050101,0x0D0D0101,0x05050909,0x0D0D0909,0x07070101,0x0F0F0101,0x07070909,0x0F0F0909,
   0x05050303,0x0D0D0303,0x05050B0B,0x0D0D0B0B,0x07070303,0x0F0F0303,0x07070B0B,0x0F0F0B0B,
   0x01010505,0x09090505,0x01010D0D,0x09090D0D,0x03030505,0x0B0B0505,0x03030D0D,0x0B0B0D0D,
   0x01010707,0x09090707,0x01010F0F,0x09090F0F,0x03030707,0x0B0B0707,0x03030F0F,0x0B0B0F0F,
   0x05050505,0x0D0D0505,0x05050D0D,0x0D0D0D0D,0x07070505,0x0F0F0505,0x07070D0D,0x0F0F0D0D,
   0x05050707,0x0D0D0707,0x05050F0F,0x0D0D0F0F,0x07070707,0x0F0F0707,0x07070F0F,0x0F0F0F0F
};

dword M1hMap[0x100] = {
   0x00000000,0x02000000,0x00020000,0x02020000,0x00000200,0x02000200,0x00020200,0x02020200,
   0x00000002,0x02000002,0x00020002,0x02020002,0x00000202,0x02000202,0x00020202,0x02020202,
   0x01000000,0x03000000,0x01020000,0x03020000,0x01000200,0x03000200,0x01020200,0x03020200,
   0x01000002,0x03000002,0x01020002,0x03020002,0x01000202,0x03000202,0x01020202,0x03020202,
   0x00010000,0x02010000,0x00030000,0x02030000,0x00010200,0x02010200,0x00030200,0x02030200,
   0x00010002,0x02010002,0x00030002,0x02030002,0x00010202,0x02010202,0x00030202,0x02030202,
   0x01010000,0x03010000,0x01030000,0x03030000,0x01010200,0x03010200,0x01030200,0x03030200,
   0x01010002,0x03010002,0x01030002,0x03030002,0x01010202,0x03010202,0x01030202,0x03030202,
   0x00000100,0x02000100,0x00020100,0x02020100,0x00000300,0x02000300,0x00020300,0x02020300,
   0x00000102,0x02000102,0x00020102,0x02020102,0x00000302,0x02000302,0x00020302,0x02020302,
   0x01000100,0x03000100,0x01020100,0x03020100,0x01000300,0x03000300,0x01020300,0x03020300,
   0x01000102,0x03000102,0x01020102,0x03020102,0x01000302,0x03000302,0x01020302,0x03020302,
   0x00010100,0x02010100,0x00030100,0x02030100,0x00010300,0x02010300,0x00030300,0x02030300,
   0x00010102,0x02010102,0x00030102,0x02030102,0x00010302,0x02010302,0x00030302,0x02030302,
   0x01010100,0x03010100,0x01030100,0x03030100,0x01010300,0x03010300,0x01030300,0x03030300,
   0x01010102,0x03010102,0x01030102,0x03030102,0x01010302,0x03010302,0x01030302,0x03030302,
   0x00000001,0x02000001,0x00020001,0x02020001,0x00000201,0x02000201,0x00020201,0x02020201,
   0x00000003,0x02000003,0x00020003,0x02020003,0x00000203,0x02000203,0x00020203,0x02020203,
   0x01000001,0x03000001,0x01020001,0x03020001,0x01000201,0x03000201,0x01020201,0x03020201,
   0x01000003,0x03000003,0x01020003,0x03020003,0x01000203,0x03000203,0x01020203,0x03020203,
   0x00010001,0x02010001,0x00030001,0x02030001,0x00010201,0x02010201,0x00030201,0x02030201,
   0x00010003,0x02010003,0x00030003,0x02030003,0x00010203,0x02010203,0x00030203,0x02030203,
   0x01010001,0x03010001,0x01030001,0x03030001,0x01010201,0x03010201,0x01030201,0x03030201,
   0x01010003,0x03010003,0x01030003,0x03030003,0x01010203,0x03010203,0x01030203,0x03030203,
   0x00000101,0x02000101,0x00020101,0x02020101,0x00000301,0x02000301,0x00020301,0x02020301,
   0x00000103,0x02000103,0x00020103,0x02020103,0x00000303,0x02000303,0x00020303,0x02020303,
   0x01000101,0x03000101,0x01020101,0x03020101,0x01000301,0x03000301,0x01020301,0x03020301,
   0x01000103,0x03000103,0x01020103,0x03020103,0x01000303,0x03000303,0x01020303,0x03020303,
   0x00010101,0x02010101,0x00030101,0x02030101,0x00010301,0x02010301,0x00030301,0x02030301,
   0x00010103,0x02010103,0x00030103,0x02030103,0x00010303,0x02010303,0x00030303,0x02030303,
   0x01010101,0x03010101,0x01030101,0x03030101,0x01010301,0x03010301,0x01030301,0x03030301,
   0x01010103,0x03010103,0x01030103,0x03030103,0x01010303,0x03010303,0x01030303,0x03030303
};

dword M2hMap[0x100] = {
   0x00000000,0x12000000,0x12000000,0x01000000,0x00120000,0x12120000,0x12120000,0x01120000,
   0x00120000,0x12120000,0x12120000,0x01120000,0x00010000,0x12010000,0x12010000,0x01010000,
   0x00001200,0x12001200,0x12001200,0x01001200,0x00121200,0x12121200,0x12121200,0x01121200,
   0x00121200,0x12121200,0x12121200,0x01121200,0x00011200,0x12011200,0x12011200,0x01011200,
   0x00001200,0x12001200,0x12001200,0x01001200,0x00121200,0x12121200,0x12121200,0x01121200,
   0x00121200,0x12121200,0x12121200,0x01121200,0x00011200,0x12011200,0x12011200,0x01011200,
   0x00000100,0x12000100,0x12000100,0x01000100,0x00120100,0x12120100,0x12120100,0x01120100,
   0x00120100,0x12120100,0x12120100,0x01120100,0x00010100,0x12010100,0x12010100,0x01010100,
   0x00000012,0x12000012,0x12000012,0x01000012,0x00120012,0x12120012,0x12120012,0x01120012,
   0x00120012,0x12120012,0x12120012,0x01120012,0x00010012,0x12010012,0x12010012,0x01010012,
   0x00001212,0x12001212,0x12001212,0x01001212,0x00121212,0x12121212,0x12121212,0x01121212,
   0x00121212,0x12121212,0x12121212,0x01121212,0x00011212,0x12011212,0x12011212,0x01011212,
   0x00001212,0x12001212,0x12001212,0x01001212,0x00121212,0x12121212,0x12121212,0x01121212,
   0x00121212,0x12121212,0x12121212,0x01121212,0x00011212,0x12011212,0x12011212,0x01011212,
   0x00000112,0x12000112,0x12000112,0x01000112,0x00120112,0x12120112,0x12120112,0x01120112,
   0x00120112,0x12120112,0x12120112,0x01120112,0x00010112,0x12010112,0x12010112,0x01010112,
   0x00000012,0x12000012,0x12000012,0x01000012,0x00120012,0x12120012,0x12120012,0x01120012,
   0x00120012,0x12120012,0x12120012,0x01120012,0x00010012,0x12010012,0x12010012,0x01010012,
   0x00001212,0x12001212,0x12001212,0x01001212,0x00121212,0x12121212,0x12121212,0x01121212,
   0x00121212,0x12121212,0x12121212,0x01121212,0x00011212,0x12011212,0x12011212,0x01011212,
   0x00001212,0x12001212,0x12001212,0x01001212,0x00121212,0x12121212,0x12121212,0x01121212,
   0x00121212,0x12121212,0x12121212,0x01121212,0x00011212,0x12011212,0x12011212,0x01011212,
   0x00000112,0x12000112,0x12000112,0x01000112,0x00120112,0x12120112,0x12120112,0x01120112,
   0x00120112,0x12120112,0x12120112,0x01120112,0x00010112,0x12010112,0x12010112,0x01010112,
   0x00000001,0x12000001,0x12000001,0x01000001,0x00120001,0x12120001,0x12120001,0x01120001,
   0x00120001,0x12120001,0x12120001,0x01120001,0x00010001,0x12010001,0x12010001,0x01010001,
   0x00001201,0x12001201,0x12001201,0x01001201,0x00121201,0x12121201,0x12121201,0x01121201,
   0x00121201,0x12121201,0x12121201,0x01121201,0x00011201,0x12011201,0x12011201,0x01011201,
   0x00001201,0x12001201,0x12001201,0x01001201,0x00121201,0x12121201,0x12121201,0x01121201,
   0x00121201,0x12121201,0x12121201,0x01121201,0x00011201,0x12011201,0x12011201,0x01011201,
   0x00000101,0x12000101,0x12000101,0x01000101,0x00120101,0x12120101,0x12120101,0x01120101,
   0x00120101,0x12120101,0x12120101,0x01120101,0x00010101,0x12010101,0x12010101,0x01010101
};

dword M3hMap[0x100] = {
   0x00000000,0x00000000,0x00000000,0x00000000,0x02020000,0x02020000,0x02020000,0x02020000,
   0x00000202,0x00000202,0x00000202,0x00000202,0x02020202,0x02020202,0x02020202,0x02020202,
   0x00000000,0x00000000,0x00000000,0x00000000,0x02020000,0x02020000,0x02020000,0x02020000,
   0x00000202,0x00000202,0x00000202,0x00000202,0x02020202,0x02020202,0x02020202,0x02020202,
   0x00000000,0x00000000,0x00000000,0x00000000,0x02020000,0x02020000,0x02020000,0x02020000,
   0x00000202,0x00000202,0x00000202,0x00000202,0x02020202,0x02020202,0x02020202,0x02020202,
   0x00000000,0x00000000,0x00000000,0x00000000,0x02020000,0x02020000,0x02020000,0x02020000,
   0x00000202,0x00000202,0x00000202,0x00000202,0x02020202,0x02020202,0x02020202,0x02020202,
   0x01010000,0x01010000,0x01010000,0x01010000,0x03030000,0x03030000,0x03030000,0x03030000,
   0x01010202,0x01010202,0x01010202,0x01010202,0x03030202,0x03030202,0x03030202,0x03030202,
   0x01010000,0x01010000,0x01010000,0x01010000,0x03030000,0x03030000,0x03030000,0x03030000,
   0x01010202,0x01010202,0x01010202,0x01010202,0x03030202,0x03030202,0x03030202,0x03030202,
   0x01010000,0x01010000,0x01010000,0x01010000,0x03030000,0x03030000,0x03030000,0x03030000,
   0x01010202,0x01010202,0x01010202,0x01010202,0x03030202,0x03030202,0x03030202,0x03030202,
   0x01010000,0x01010000,0x01010000,0x01010000,0x03030000,0x03030000,0x03030000,0x03030000,
   0x01010202,0x01010202,0x01010202,0x01010202,0x03030202,0x03030202,0x03030202,0x03030202,
   0x00000101,0x00000101,0x00000101,0x00000101,0x02020101,0x02020101,0x02020101,0x02020101,
   0x00000303,0x00000303,0x00000303,0x00000303,0x02020303,0x02020303,0x02020303,0x02020303,
   0x00000101,0x00000101,0x00000101,0x00000101,0x02020101,0x02020101,0x02020101,0x02020101,
   0x00000303,0x00000303,0x00000303,0x00000303,0x02020303,0x02020303,0x02020303,0x02020303,
   0x00000101,0x00000101,0x00000101,0x00000101,0x02020101,0x02020101,0x02020101,0x02020101,
   0x00000303,0x00000303,0x00000303,0x00000303,0x02020303,0x02020303,0x02020303,0x02020303,
   0x00000101,0x00000101,0x00000101,0x00000101,0x02020101,0x02020101,0x02020101,0x02020101,
   0x00000303,0x00000303,0x00000303,0x00000303,0x02020303,0x02020303,0x02020303,0x02020303,
   0x01010101,0x01010101,0x01010101,0x01010101,0x03030101,0x03030101,0x03030101,0x03030101,
   0x01010303,0x01010303,0x01010303,0x01010303,0x03030303,0x03030303,0x03030303,0x03030303,
   0x01010101,0x01010101,0x01010101,0x01010101,0x03030101,0x03030101,0x03030101,0x03030101,
   0x01010303,0x01010303,0x01010303,0x01010303,0x03030303,0x03030303,0x03030303,0x03030303,
   0x01010101,0x01010101,0x01010101,0x01010101,0x03030101,0x03030101,0x03030101,0x03030101,
   0x01010303,0x01010303,0x01010303,0x01010303,0x03030303,0x03030303,0x03030303,0x03030303,
   0x01010101,0x01010101,0x01010101,0x01010101,0x03030101,0x03030101,0x03030101,0x03030101,
   0x01010303,0x01010303,0x01010303,0x01010303,0x03030303,0x03030303,0x03030303,0x03030303
};


void update_skew(void)
{
   new_dt.NewHDSPTIMG |= 0x02; // enable horizontal DISPTMG by default
   dword skew = (CRTC.registers[8] >> 4) & 3; // isolate the display skew
   if (skew == 3) { // disable output?
      new_dt.NewHDSPTIMG &= 0xfd; // disable horizontal DISPTMG
   } else {
      CRTC.hstart = skew; // position at which horizontal display starts
      CRTC.hend = CRTC.hstart + CRTC.registers[1]; // position at which it ends
   }
}



inline void change_mode(void)
{
   if (CRTC.flag_hadhsync) { // have we had an HSYNC on this scan line?
      CRTC.flag_hadhsync = 0;
      GateArray.scr_mode = GateArray.requested_scr_mode; // execute mode change
      ModeMap = ModeMaps[GateArray.scr_mode]; // update ModeMap pointer
   }
}



inline void end_vdu_hsync(void)
{
   int temp;

   CRTC.flag_inmonhsync = 0;
   iMonHSPeakToEnd = iMonHSPeakPos;
   if (HadP) {
      HadP = 0;
      if (iMonHSPeakPos >= iMonHSStartPos) {
         temp = iMonHSEndPos - HSyncDuration;
         if (temp < MonFreeSync) {
            if (MonFreeSync != MinHSync) {
               MonFreeSync--;
            }
         } else if (temp > MonFreeSync) {
            if (MonFreeSync != MaxHSync) {
               MonFreeSync++;
            }
         }
         temp = iMonHSPeakToEnd - iMonHSEndToPeak;
         if (temp < 0) {
            temp = -temp;
            if (temp > iMonHSStartPos) {
               temp = iMonHSStartPos;
            }
            temp >>= 3;
            if (!temp) {
               temp++;
            }
            if (temp > MAX_SYNC_INC) {
               temp = MAX_SYNC_INC;
            }
            MonHSYNC = MonFreeSync + temp;
            if (MonHSYNC > MaxHSync) {
               MonHSYNC = MaxHSync;
            }
         } else {
            if (temp > iMonHSStartPos) {
               temp = iMonHSStartPos;
            }
            temp >>= 3;
            if (!temp) {
               temp++;
            }
            if (temp > MAX_SYNC_DEC) {
               temp = MAX_SYNC_DEC;
            }
            MonHSYNC = MonFreeSync - temp;
            if (MonHSYNC < MinHSync) {
               MonHSYNC = MinHSync;
            }
         }
      } else {
         temp = iMonHSStartToPeak - iMonHSPeakToEnd;
         if (!temp) {
            MonHSYNC = MonFreeSync;
         } else if (temp < 0) {
            temp = -temp;
            if (temp > iMonHSStartPos) {
               temp = iMonHSStartPos;
            }
            temp >>= 3;
            if (!temp) {
               temp++;
            }
            if (temp > MAX_SYNC_INC) {
               temp = MAX_SYNC_INC;
            }
            MonHSYNC = MonFreeSync + temp;
            if (MonHSYNC > MaxHSync) {
               MonHSYNC = MaxHSync;
            }
         } else if (temp > 0) {
            if (temp > iMonHSStartPos) {
               temp = iMonHSStartPos;
            }
            temp >>= 3;
            if (!temp) {
               temp++;
            }
            if (temp > MAX_SYNC_DEC) {
               temp = MAX_SYNC_DEC;
            }
            MonHSYNC = MonFreeSync - temp;
            if (MonHSYNC < MinHSync) {
               MonHSYNC = MinHSync;
            }
         }
      }
   }
   iMonHSEndPos = 0;
}



inline void match_line_count(void)
{
   if (CRTC.line_count == CRTC.registers[6]) { // matches vertical displayed?
      new_dt.NewDISPTIMG = 0; // disable vertical DISPTMG
   }
   if (CRTC.line_count == CRTC.registers[7]) { // matches vertical sync position?
      if (CRTC.last_hend >= 2) { // line length was at least 2 chars?
         if (!CRTC.r7match) {
            CRTC.flag_resvsync = 0;
            if (!CRTC.flag_invsync) { // not in VSYNC?
               CRTC.vsw_count = 0; // reset vertical sync width counter
               CRTC.flag_invsync = 1; // enter VSYNC
               flags1.monVSYNC = 26; // enter vertical blanking period for 26 scanlines
               GateArray.hs_count = 2; // GA delays its VSYNC by two CRTC HSYNCs
            }
         }
      }
      CRTC.r7match = 1;
   } else {
      CRTC.r7match = 0;
   }
}



inline void reload_addr(void)
{
   if (CRTC.line_count == 0) { // has line count been reset?
      new_dt.NewDISPTIMG = 0xff; // enable vertical DISPTMG
      CRTC.addr =
      CRTC.next_addr = CRTC.requested_addr; // load with screen start address
   }
   match_line_count();
}



inline void restart_frame(void)
{
   CRTC.flag_invta = 0;
   CRTC.flag_resframe = 0;
   CRTC.flag_resscan = 0;
   CRTC.flag_reschar = 0;
   CRTC.raster_count = 0; // reset raster line counter
   CRTC.scr_base = 0;
   CRTC.line_count = 0; // reset character line counter
   reload_addr();
}



inline void match_hsw(void)
{
   if (CRTC.hsw_count == CRTC.hsw) { // matches horizontal sync width?
      GateArray.sl_count++; // update GA scan line counter
      if (GateArray.sl_count == 52) { // trigger interrupt?
         z80.int_pending = 1; // queue Z80 interrupt
         GateArray.sl_count = 0; // clear counter
      }
      if (GateArray.hs_count) { // delaying on VSYNC?
         GateArray.hs_count--;
         if (!GateArray.hs_count) {
            if (GateArray.sl_count >= 32) { // counter above save margin?
               z80.int_pending = 1; // queue interrupt
            }
            GateArray.sl_count = 0; // clear counter
         }
      }
      flags1.inHSYNC = 0; // turn HSYNC off
      if (flags1.monVSYNC) { // in vertical blanking period?
         flags1.monVSYNC--; // update counter
      }
      change_mode(); // process possible mode change
      if (CRTC.flag_inmonhsync) { // monitor HSYNC still active?
         end_vdu_hsync();
      }
   } else {
      CRTC.hsw_count++; // update counter
      CRTC.hsw_count &= 15; // limit to 4 bits
      if (CRTC.hsw_count == 3) { // ready to start monitor HSYNC?
         CRTC.flag_inmonhsync = 1; // enter monitor HSYNC
         iMonHSStartPos = 0;
         iMonHSPeakToStart = iMonHSPeakPos;
      } else if (CRTC.hsw_count == 7) { // reached GA HSYNC output cutoff?
         change_mode();
         end_vdu_hsync();
      }
   }
}



void NoChar(void)
{
   // nothing to do
}



void CharSL2(void)
{
   CRTC.reg5 = CRTC.registers[5];
   CRTC.CharInstSL = (void(*)(void))NoChar;
}



void CharSL1(void)
{
   CRTC.CharInstSL = (void(*)(void))CharSL2;
}



void CharMR2(void)
{
   if (CRTC.flag_startvta) { // starting vertical total adjust?
      if (CRTC.line_count == CRTC.registers[4]) { // matches vertical total?
         if (CRTC.registers[5] == 0) { // no vertical total adjust?
            CRTC.flag_resnext = 1; // request a frame restart
         }
      }
   }
   CRTC.CharInstMR = (void(*)(void))NoChar;
}



void CharMR1(void)
{
   if ((CRTC.raster_count == CRTC.registers[9]) && (CRTC.line_count == CRTC.registers[4])) {
      CRTC.flag_invta = 0;
      CRTC.flag_startvta = 1; // request start of vertical total adjust
   } else {
      CRTC.flag_startvta = 0; // not yet at end of frame
   }
   CRTC.CharInstMR = (void(*)(void))CharMR2;
}



void frame_finished(void)
{
/*   if (VDU.scrln < MAX_DRAWN) { // monitor line below maximum visible?
      int cnt = (MAX_DRAWN - VDU.scrln) << 1; // number of lines remaining to be drawn
      dword *addr = CPC.scr_base;
      while (cnt--) {
         dword *tmp_addr = addr;
         for (int n = CPC.scr_bpp * 24; n; n--) {
            *tmp_addr++ = 0; // clear old surface contents
         }
         addr += CPC.scr_bps; // advance surface pointer
      }
   }
*/   VDU.frame_completed = 1; // emulation loop exit condition met
   VDU.scrln = -(((VDU.scanline - MIN_VHOLD) + 1) >> 1);
   VDU.scanline = 0;
   VDU.flag_drawing = 0;
}



void prerender_border(void)
{
   register dword dwVal = 0x10101010;
   *RendPos = dwVal;
   *(RendPos + 1) = dwVal;
  *(RendPos + 2) = dwVal;
   *(RendPos + 3) = dwVal;
   RendPos += 4;
}



void prerender_border_half(void)
{
   register dword dwVal = 0x10101010;
   *RendPos = dwVal;
   *(RendPos + 1) = dwVal;
   RendPos += 2;
}



void prerender_sync(void)
{
   register dword dwVal = 0x11111111;
   *RendPos = dwVal;
   *(RendPos + 1) = dwVal;
   *(RendPos + 2) = dwVal;
   *(RendPos + 3) = dwVal;
   RendPos += 4;
}



void prerender_sync_half(void)
{
   register dword dwVal = 0x11111111;
   *RendPos = dwVal;
   *(RendPos + 1) = dwVal;
   RendPos += 2;
}



void prerender_normal(void)
{
   register byte bVidMem = *(pbRAM + CRTC.next_address);
   *RendPos = *(ModeMap + (bVidMem * 2));
   *(RendPos + 1) = *(ModeMap + (bVidMem * 2) + 1);
   bVidMem = *(pbRAM + CRTC.next_address + 1);
   *(RendPos + 2) = *(ModeMap + (bVidMem * 2));
   *(RendPos + 3) = *(ModeMap + (bVidMem * 2) + 1);
   RendPos += 4;
}



void prerender_normal_half(void)
{
   register byte bVidMem = *(pbRAM + CRTC.next_address);
   *RendPos = *(ModeMap + bVidMem);
   bVidMem = *(pbRAM + CRTC.next_address + 1);
   *(RendPos + 1) = *(ModeMap + bVidMem);
   RendPos += 2;
}



void set_prerender(void)
{
   LastPreRend = flags1.combined;
   if (LastPreRend == 0x03ff0000) {
      PreRender = CPC.scr_prerendernorm;
   } else {
      if (!(word)LastPreRend) {
         PreRender = CPC.scr_prerenderbord;
      } else {
         PreRender = CPC.scr_prerendersync;
      }
   }
}



void render8bpp(void)
{
   register byte *pbPos = (byte *)CPC.scr_pos;
   register byte bCount = *RendWid++;
   while (bCount--) {
      *pbPos++ = GateArray.palette[*RendOut++];
   }
   CPC.scr_pos = (dword *)pbPos;
}



void render8bpp_doubleY(void)
{
   register byte *pbPos = (byte *)CPC.scr_pos;
   register dword dwLineOffs = CPC.scr_bps << 2;
   register byte bCount = *RendWid++;
   while (bCount--) {
      register byte val = GateArray.palette[*RendOut++];
      *(pbPos + dwLineOffs) = val;
      *pbPos++ = val;
   }
   CPC.scr_pos = (dword *)pbPos;
}



void render16bpp(void)
{
   register word *pwPos = (word *)CPC.scr_pos;
   register byte bCount = *RendWid++;
   while (bCount--) {
      *pwPos++ = GateArray.palette[*RendOut++];
   }
   CPC.scr_pos = (dword *)pwPos;
}



void render16bpp_doubleY(void)
{
   register word *pwPos = (word *)CPC.scr_pos;
   register dword dwLineOffs = CPC.scr_bps << 1;
   register byte bCount = *RendWid++;
   while (bCount--) {
      register word val = GateArray.palette[*RendOut++];
      *(pwPos + dwLineOffs) = val;
      *pwPos++ = val;
   }
   CPC.scr_pos = (dword *)pwPos;
}



void render24bpp(void)
{
   register byte *pbPos = (byte *)CPC.scr_pos;
   register byte bCount = *RendWid++;
   while (bCount--) {
      register dword val = GateArray.palette[*RendOut++];
      *(word *)pbPos = (word)val;
      *(pbPos + 2) = (byte)(val >> 16);
      pbPos += 3;
   }
   CPC.scr_pos = (dword *)pbPos;
}



void render24bpp_doubleY(void)
{
   register byte *pbPos = (byte *)CPC.scr_pos;
   register dword dwLineOffs = CPC.scr_bps << 2;
   register byte bCount = *RendWid++;
   while (bCount--) {
      register dword val = GateArray.palette[*RendOut++];
      *(word *)(pbPos + dwLineOffs) = (word)val;
      *(word *)pbPos = (word)val;
      val >>= 16;
      *(pbPos + dwLineOffs + 2) = (byte)val;
      *(pbPos + 2) = (byte)val;
      pbPos += 3;
   }
   CPC.scr_pos = (dword *)pbPos;
}



void render32bpp(void)
{
   register byte bCount = *RendWid++;
   while (bCount--) {
      *CPC.scr_pos++ = GateArray.palette[*RendOut++];
   }
}



void render32bpp_doubleY(void)
{
   register byte bCount = *RendWid++;
   while (bCount--) {
      register dword val = GateArray.palette[*RendOut++];
      *(CPC.scr_pos + CPC.scr_bps) = val;
      *CPC.scr_pos++ = val;
   }
}



void crtc_cycle(int repeat_count)
{
   while (repeat_count) {
      if (VDU.flag_drawing) { // are we within the rendering area?
         if (HorzChar < HorzMax) { // below horizontal cut-off?
            if (flags1.combined != LastPreRend) {
               set_prerender(); // change pre-renderer if necessary
            }
            PreRender(); // translate CPC video memory bytes to entries referencing the palette
            CPC.scr_render(); // render to the video surface at the current bit depth
         }
      }
      CRTC.next_address = MAXlate[(CRTC.addr + CRTC.char_count) & 0x73ff] | CRTC.scr_base; // next address for PreRender
      flags1.dt.combined = new_dt.combined; // update the DISPTMG flags

      #ifdef DEBUG_CRTC
      if (dwDebugFlag) {
         char str[16];
         char on[]  = "vhDDHVMa";
         char off[] = "........";

         if (CRTC.flag_invsync) {
            str[0] = on[0];
         } else {
            str[0] = off[0];
         }
         if (flags1.inHSYNC) {
            str[1] = on[1];
         } else {
            str[1] = off[1];
         }
         if (flags1.dt.HDSPTIMG & 1) {
            str[2] = on[2];
         } else {
            str[2] = off[2];
         }
         if (flags1.dt.DISPTIMG) {
            str[3] = on[3];
         } else {
            str[3] = off[3];
         }
         if (CRTC.flag_newscan) {
            str[4] = on[4];
         } else {
            str[4] = off[4];
         }
         if (CRTC.flag_resframe) {
            str[5] = on[5];
         } else {
            str[5] = off[5];
         }
         if (CRTC.flag_resscan) {
            str[6] = on[6];
         } else {
            str[6] = off[6];
         }
         if (CRTC.flag_invta) {
            str[7] = on[7];
         } else {
            str[7] = off[7];
         }
         str[8] = '\0';

         fprintf(pfoDebug, "%04X | CC:%2X RC:%2X LC:%2X - HSWC:%2X VSWC:%2X - %s %c %2X  mhs%04X mfs%04X  s%04X p%04X e%04X p%04X c%d\r\n",
            CRTC.next_address,
            CRTC.char_count,
            CRTC.raster_count,
            CRTC.line_count,
            CRTC.hsw_count,
            CRTC.vsw_count,
            str,
            z80.int_pending ? 'I' : '.',
            GateArray.sl_count,
            MonHSYNC,
            MonFreeSync,
            iMonHSStartPos,
            iMonHSPeakPos,
            iMonHSEndPos,
            HorzPos,
            HorzChar);
      }
      #endif

      iMonHSStartPos += 0x100;
      iMonHSEndPos += 0x100;
      iMonHSPeakPos += 0x100;
      HorzPos += 0x100;
      HorzChar++;
      if (HorzPos >= MonHSYNC) {
         if (VDU.flag_drawing) {
            CPC.scr_base += CPC.scr_line_offs; // advance surface pointer to next row
         }
         HadP = 1;
         iMonHSPeakPos = HorzPos - MonHSYNC;
         iMonHSStartToPeak = iMonHSStartPos - iMonHSPeakPos;
         iMonHSEndToPeak = iMonHSEndPos - iMonHSPeakPos;
         HorzPos = iMonHSPeakPos - HSyncDuration;

         HorzChar = HorzPos >> 8;
         dword val = (HorzPos & 0xf0) >> PosShift;
         if (!val) {
            HorzMax = 48;
            HorzPix[0] = HorzPix[1];
            RendPos = RendStart;
            HorzChar--;
         } else {
            RendPos = (dword *)&RendBuff[val];
            int tmp = (byte *)RendStart - (byte *)RendPos;
            HorzPix[48] = (byte)tmp;
            HorzPix[0] = HorzPix[1] - (byte)tmp;
            HorzMax = 49;
         }
         RendOut = (byte *)RendStart;
         RendWid = &HorzPix[0];
         CPC.scr_pos = CPC.scr_base;
         VDU.scrln++;
         VDU.scanline++;
         if ((dword)VDU.scrln >= MAX_DRAWN) {
            VDU.flag_drawing = 0;
         } else {
            VDU.flag_drawing = 1;
         }
      }

// ----------------------------------------------------------------------------

      if (CRTC.char_count == CRTC.registers[0]) { // matches horizontal total?
         CRTC.last_hend = CRTC.char_count; // preserve current line length in chars
         CRTC.flag_newscan = 1; // request starting a new scan line
         CRTC.char_count = 0; // reset the horizontal character count
      } else {
         CRTC.char_count++; // update counter
         CRTC.char_count &= 255; // limit to 8 bits
      }

      if (CRTC.char_count == CRTC.registers[0]) { // matches horizontal total?
         if (CRTC.raster_count == CRTC.registers[9]) { // matches maximum raster address?
            CRTC.flag_reschar = 1; // request a line count update
         } else {
            CRTC.flag_reschar = 0; // still within the current character line
         }
         if (CRTC.flag_resnext) { // ready to restart frame?
            CRTC.flag_resnext = 0;
            CRTC.flag_resframe = 1; // request a frame restart
         }
         if (CRTC.flag_startvta) { // ready to start vertical total adjust?
            CRTC.flag_startvta = 0;
            CRTC.flag_invta = 1; // entering vertical total adjust
         }
         if (CRTC.flag_invta) { // in vertical total adjust?
            if ((CRTC.raster_count == CRTC.registers[9]) && (CRTC.line_count == CRTC.registers[4])) {
               CRTC.flag_resscan = 1; // raster counter only resets once at start of vta
            } else {
               CRTC.flag_resscan = 0; // raster counter keeps increasing while in vta
            }
         }
      }

      if (CRTC.char_count == CRTC.registers[1]) { // matches horizontal displayed?
         if (CRTC.raster_count == CRTC.registers[9]) { // matches maximum raster address?
            CRTC.next_addr = CRTC.addr + CRTC.char_count;
         }
      }

      if (!flags1.inHSYNC) { // not in HSYNC?
         if (CRTC.char_count == CRTC.registers[2]) { // matches horizontal sync position?
            flags1.inHSYNC = 0xff; // turn HSYNC on
            CRTC.flag_hadhsync = 1; // prevent GA from processing more than one HSYNC per scan line
            CRTC.hsw_count = 0; // initialize horizontal sync width counter
            match_hsw();
         }
      } else {
         match_hsw();
      }

      CRTC.CharInstSL(); // if necessary, process vertical total delay
      CRTC.CharInstMR(); // if necessary, process maximum raster count delay

      if (CRTC.flag_newscan) { // scanline change requested?
         CRTC.flag_newscan = 0;
         CRTC.addr = CRTC.next_addr;

         if (CRTC.flag_invsync) { // VSYNC active?
            CRTC.vsw_count++; // update counter
            CRTC.vsw_count &= 15; // limit to 4 bits
            if (CRTC.vsw_count == CRTC.vsw) { // matches vertical sync width?
               CRTC.vsw_count = 0; // reset counter
               CRTC.flag_resvsync = 1; // request VSYNC reset
            }
         }

         if (CRTC.flag_resframe) { // frame restart requested?
            restart_frame();
         } else {
            if (CRTC.flag_resscan) { // raster counter reset requested?
               CRTC.flag_resscan = 0;
               CRTC.raster_count = 0; // reset counter
               CRTC.scr_base = 0;
            } else {
               CRTC.raster_count++; // update counter
               CRTC.raster_count &= 31; // limit to 5 bits
               if (!CRTC.raster_count) { // did the counter wrap around?
                  match_line_count();
               }
               CRTC.scr_base = (CRTC.scr_base + 0x0800) & 0x3800;
            }
         }

         CRTC.CharInstSL = (void(*)(void))CharSL1;

         register dword temp = 0;
         if (CRTC.raster_count == CRTC.registers[9]) { // matches maximum raster address?
            temp = 1;
            CRTC.flag_resscan = 1; // request a raster counter reset
         }
         if (CRTC.r9match != temp) {
            CRTC.r9match = temp;
         }
         if (temp) {
            CRTC.CharInstMR = (void(*)(void))CharMR1;
         }

         if (CRTC.flag_invta) { // in vertical total adjust?
            if (CRTC.raster_count == CRTC.reg5) { // matches vertical total adjust?
               restart_frame();
               if (CRTC.registers[9] == 0) { // maximum raster address is zero?
                  CRTC.flag_resscan = 1; // request a raster counter reset
               }
            }
         }

         if (CRTC.flag_reschar) { // line count update requested?
            CRTC.line_count++; // update counter
            CRTC.line_count &= 127; // limit to 7 bits
            reload_addr();
         }

         if (CRTC.flag_invsync) { // in VSYNC?
            if (CRTC.flag_resvsync) { // end of VSYNC?
               CRTC.flag_invsync = 0; // turn VSYNC off
               CRTC.flag_resvsync = 0;
               if (VDU.scanline == MaxVSync) { // above maximum scanline count?
                  frame_finished();
               }
            } else {
               if (VDU.scanline > MinVSync) { // above minimum scanline count?
                  frame_finished();
               }
            }
         } else if (VDU.scanline == MaxVSync) { // above maximum scanline count?
            frame_finished();
         }
      }

      if (CRTC.char_count == CRTC.hstart) { // leaving border area?
         new_dt.NewHDSPTIMG |= 0x01;
      }
      if (CRTC.char_count == CRTC.hend) { // entering border area?
         new_dt.NewHDSPTIMG &= 0xfe;
      }

// ----------------------------------------------------------------------------

      repeat_count--;
   }
}



void crtc_init(void)
{
   if (dwXScale == 1) {
      ModeMaps[0] = M0hMap;
      ModeMaps[1] = M1hMap;
      ModeMaps[2] = M2hMap;
      ModeMaps[3] = M3hMap;
   } else {
      ModeMaps[0] = M0Map;
      ModeMaps[1] = M1Map;
      ModeMaps[2] = M2Map;
      ModeMaps[3] = M3Map;
   }
   ModeMap = ModeMaps[0];
   for (int l = 0; l < 0x7400; l++) {
      int j = l << 1; // actual address
      MAXlate[l] = (j & 0x7FE) | ((j & 0x6000) << 1);
   }
}



void crtc_reset(void)
{
   int Wid;

   memset(&CRTC, 0, sizeof(CRTC)); // clear CRTC data structure
   CRTC.registers[0] = 0x3f;
   CRTC.registers[2] = 0x2e;
   CRTC.registers[3] = 0x8e;

   if (dwXScale == 1) {
      Wid = 8;
      PosShift = 5;
   } else {
      Wid = 16;
      PosShift = 4;
   }
   for (int i = 0; i < 48; i++) {
      HorzPix[i] = Wid;
   }
   HorzPix[48] = 0;
   RendStart = (dword *)&RendBuff[Wid];
   RendPos = (dword *)&RendBuff[0];
   RendOut = (byte *)RendStart;
   RendWid = &HorzPix[0];

   HorzPos = 0x500;
   HorzChar = 0x04;
   HorzMax = 48;
   HSyncDuration = 0xA00;
   MinHSync = 0x4000 - HSyncDuration - 257;
   MaxHSync = 0x4000 - HSyncDuration + 257;
   MonHSYNC = 0x4000 - HSyncDuration;
   MonFreeSync = MonHSYNC;
   flags1.monVSYNC = 0;
   flags1.dt.DISPTIMG = 0xff;
   flags1.dt.HDSPTIMG = 0x03;
   new_dt.NewDISPTIMG = 0xff;
   new_dt.NewHDSPTIMG = 0x03;
   CRTC.CharInstSL = (void(*)(void))NoChar;
   CRTC.CharInstMR = (void(*)(void))NoChar;

   MinVSync = MID_VHOLD;
   MaxVSync = MinVSync + MIN_VHOLD_RANGE + (int)ceil((float)((MinVSync - MIN_VHOLD) *
    (MAX_VHOLD_RANGE - MIN_VHOLD_RANGE) / (MAX_VHOLD - MIN_VHOLD)));
}
